package net.yters.model;
import java.io.Serializable;
import java.util.ArrayList;



public class Environment implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -2982276430741634223L;

	public enum AGNT {EMPT, WALL, HERO, BLLT, XTRN, RAND, SMTC, MIMC}; // AGNT stands for agent type
	
	public AGNT[][] invariant_env =  {{AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.EMPT,AGNT.WALL},
                                      {AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL,AGNT.WALL}};

	// TODO should have an explicit conversion utility to demarcate when the values are being used semantically vs syntactically
	ArrayList<ArrayList<Double>> env = new ArrayList<ArrayList<Double>>(); // NOTE doubles are used b/c that is what agents use to evaluate each other
	
	public Environment() {
		refresh();
	}
	
	public void refresh() {
		env = new ArrayList<ArrayList<Double>>();
		
		for(int r = 0; r < invariant_env.length; r++) {
			env.add(new ArrayList<Double>());
			
			for(int c = 0; c < invariant_env[r].length; c++) {
				env.get(r).add(new Double(invariant_env[r][c].ordinal()));
			}
		}
	}
	
	@Override
	public String toString() {
		StringBuilder s = new StringBuilder();
		
		for(int r = 0; r < invariant_env.length; r++) {
			StringBuilder s0 = new StringBuilder();
			StringBuilder s1 = new StringBuilder();
			StringBuilder s2 = new StringBuilder();

			// NOTE convert from agent syntactic view to 
			// programmer semantic view for coding purposes
			for(int c = 0; c < invariant_env[r].length; c++)
			{
				String rep = "";
				switch(AGNT.values()[(int) Math.round(env.get(r).get(c))]) { 
				case EMPT: // blank
					rep = " ";
					break;
				case WALL: // wall
					rep = "+";
					break;
				case HERO: // hero
					rep = "@";
					break;
				case BLLT: // bullet
					rep = "o";
					break;
				case RAND: // random_enemy
					rep = "x";
					break;
				case SMTC: // semantic_enemy
					rep = "!";
					break;
				case MIMC: // ID_mimic_enemy
					rep = "a";
					break;
				default:
				}
				
				double raw = env.get(r).get(c);
				double raw_direction = Math.round((raw - Math.round(raw)) * 100.0);
				int direction = (int) raw_direction;
				
				System.err.println("rep: " + rep); // DEBUG
				System.err.println("raw: " + raw); // DEBUG
				System.err.println("raw_direction: " + raw_direction); // DEBUG
				System.err.println("direction: " + direction); // DEBUG
				
				/*
				 * Direction is yx, where y and x are 
				 * each a digit from the following 
				 * figure.
				 * 
				 * 		        x
				 *     		  0 1 2
				 *   		0   ^   0
				 * 		  y 1 < @ > 1 y
				 *   		2   v   2
				 *            0 1 2
				 *              x
				 *            
				 * So, the following directions are 
				 * encoded:
				 * ^ = 01, < = 10, v = 21, > = 12
				 */
				
				switch(direction) {
				case 01:
					s0.append(     "/^\\"     );
					s1.append(" " + rep + " ");
					s2.append(     "   "     );
					break;
				case 10:
					s0.append(     "/  "     );
					s1.append("<" + rep + " ");
					s2.append(     "\\  "     );
					break;
				case 12:
					s0.append(     "  \\"     );
					s1.append(" " + rep + ">");
					s2.append(     "  /"     );
					break;
				case 21:
					s0.append(     "   "     );
					s1.append(" " + rep + " ");
					s2.append(     "\\v/"     );
					break;
				default: 
					s0.append(     "   "     );
					s1.append(" " + rep + " ");
					s2.append(     "   "     );
					break;
				}
			}
			
			s0.append("\n");
			s1.append("\n");
			s2.append("\n");
			
			s.append(s0.toString());
			s.append(s1.toString());
			s.append(s2.toString());
		}
		
		System.err.println(s.toString()); // DEBUG
				
		return s.toString();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public int getContent(ArrayList pos) {
		Object currPos = env;
		
		for(Object i : pos) {
			currPos = ((ArrayList<Object>) currPos).get((int) Math.round((Double) i));
		}
		
		return (int) Math.round((Double) currPos);
	}
	
	@SuppressWarnings({ "unchecked" })
	void putContent(ArrayList<Object> pos, Double type) {
		Object currPos = env;
		
		for(Object i : pos.subList(0, pos.size()-1)) {
			currPos = ((ArrayList<Object>) currPos).get((int) Math.round((Double) i));
		}
		
		((ArrayList<Object>) currPos).set((int) Math.round((Double) pos.get(pos.size() - 1)), type);
	}
	
	public ArrayList<Double> getDimensions (){
		ArrayList<Double> dim = new ArrayList<Double>();
		dim.add((double) invariant_env.length); // TODO this should be changed to use ArrayList
		dim.add((double) invariant_env[0].length); // TODO this should be changed to use ArrayList
		return dim;
	}
}
